package com.bifang.core.qrcode.integration.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.bifang.common.constant.CacheConst;
import com.bifang.common.constant.enums.common.AppTypeEnum;
import com.bifang.common.constant.enums.common.ApplyTypeEnum;
import com.bifang.common.constant.enums.common.BoolEnum;
import com.bifang.common.constant.enums.common.CodeSourceTypeEnum;
import com.bifang.common.constant.enums.common.CodeTypeEnum;
import com.bifang.common.constant.enums.common.ErrorEnum;
import com.bifang.common.constant.enums.common.FuncTypeEnum;
import com.bifang.common.exception.AppException;
import com.bifang.common.helper.RedisHelper;
import com.bifang.common.util.SessionUtil;
import com.bifang.common.vo.AppCodeVO;
import com.bifang.common.vo.CodeCreateVO;
import com.bifang.common.vo.CodeDetailVO;
import com.bifang.common.annotations.ApiService;
import com.bifang.common.annotations.OpApi;
import com.bifang.core.dao.model.AppCode;
import com.bifang.core.dao.model.AppCodeHistory;
import com.bifang.core.qrcode.integration.AppCodeIntegration;
import com.bifang.core.dao.service.AppCodeHistoryService;
import com.bifang.core.dao.service.AppCodeService;
import com.bifang.core.qrcode.util.CodeUtil;
import java.awt.image.BufferedImage;
import java.util.Date;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@ApiService(funcCode = "APPCODE", title = "码管理功能管理")
public class AppCodeIntegrationImpl implements AppCodeIntegration {
    @Autowired
    RedisHelper redisHelper;
    @Autowired AppCodeService appCodeService;
    @Autowired AppCodeHistoryService appCodeHistoryService;

    @Override
    @OpApi(
            funcCode = "APPCODE0001",
            title = "获取二维码图片",
            funcType = FuncTypeEnum.other,
            savelog = false,
            checkPrivilege = BoolEnum.FALSE)
    public BufferedImage getQRCode(CodeCreateVO codeCreateVO) {

        if (StringUtils.isBlank(codeCreateVO.getCodeType())) {
            throw new AppException("码类型不能为空");
        }
        if (StringUtils.isBlank(codeCreateVO.getApplyUserId())) {
            codeCreateVO.setApplyUserId(SessionUtil.getUserId());
        }

        if (StringUtils.isBlank(codeCreateVO.getAppType())) {
            codeCreateVO.setAppType(AppTypeEnum.other.getType());
        }
        if (StringUtils.isBlank(codeCreateVO.getApplyType())) {
            codeCreateVO.setApplyType(ApplyTypeEnum.ohter.getType());
        }
        AppCodeVO appCodeVO = getCode(codeCreateVO);
        return CodeUtil.getCodeImg(appCodeVO);
    }

    @Override
    public AppCodeVO getCode(CodeCreateVO codeCreateVO) {

        Long redisTime = codeCreateVO.getRedisTime();
        if (redisTime == null) {
            CodeTypeEnum codeTypeEnum = CodeTypeEnum.getByType(codeCreateVO.getCodeType());
            if (codeTypeEnum == null) {
                throw new AppException("码类型未定义");
            }
            redisTime = codeTypeEnum.getRedisTime();
        }

        if (StringUtils.isBlank(codeCreateVO.getApplyUserId())) {
            if (StringUtils.isBlank(codeCreateVO.getApplyClientId())) {
                throw new AppException("申请用户id不能为空或者通过接口可用clientId");
            } else {
                codeCreateVO.setApplyUserId(codeCreateVO.getApplyClientId());
            }
        }
        if (StringUtils.isBlank(codeCreateVO.getSourceType())) {
            codeCreateVO.setSourceType(CodeSourceTypeEnum.local.getType());
        }

        if (StringUtils.isBlank(codeCreateVO.getAppType())) {
            throw new AppException("app类型不能为空");
        }
        if (StringUtils.isBlank(codeCreateVO.getApplyType())) {
            throw new AppException("申请渠道类型不能为空");
        }
        AppCodeVO appCodeVO = null;
        // 获取缓存中的二维码
        String key =
                codeCreateVO.getCodePro()
                        + codeCreateVO.getCodeType()
                        + codeCreateVO.getUniqueId()
                        + codeCreateVO.getApplyType()
                        + codeCreateVO.getAppType()
                        + codeCreateVO.getApplyUserId();
        if (StringUtils.isNotBlank(codeCreateVO.getApplyClientId())) {
            key = key + codeCreateVO.getApplyClientId();
        }
        String getCodeKey = CacheConst.SYS_QRCODE_GET_CODE + SecureUtil.md5(key);
        String code = "";
        if (redisHelper.hasKey(getCodeKey)) {
            code = (String) redisHelper.get(getCodeKey);
        }
        if (StringUtils.isNotBlank(code) && redisHelper.hasKey(CacheConst.SYS_QRCODE_RS + code)) {
            appCodeVO = (AppCodeVO) redisHelper.get(CacheConst.SYS_QRCODE_RS + code);
            if (StringUtils.isNotBlank(codeCreateVO.getCodeColor())) {
                appCodeVO.setCodeColor(codeCreateVO.getCodeColor());
            }
            if (StringUtils.isNotBlank(codeCreateVO.getBackColor())) {
                appCodeVO.setBackColor(codeCreateVO.getBackColor());
            }
            if (StringUtils.isNotBlank(codeCreateVO.getImgPath())) {
                appCodeVO.setImgPath(codeCreateVO.getImgPath());
            }
        } else {
            QueryWrapper<AppCode> queryWrapper = new QueryWrapper<>();
            queryWrapper
                    .lambda()
                    .eq(AppCode::getCodePro, codeCreateVO.getCodePro())
                    .eq(AppCode::getCodeType, codeCreateVO.getCodeType())
                    .eq(AppCode::getUniqueId, codeCreateVO.getUniqueId())
                    .eq(AppCode::getApplyType, codeCreateVO.getApplyType())
                    .eq(AppCode::getAppType, codeCreateVO.getAppType());
            if (StringUtils.isNotBlank(codeCreateVO.getApplyUserId())) {
                queryWrapper.lambda().eq(AppCode::getApplyUserId, codeCreateVO.getApplyUserId());
            }
            if (StringUtils.isBlank(codeCreateVO.getApplyClientId())) {
                queryWrapper.lambda().isNotNull(AppCode::getApplyClientId);
            } else {
                queryWrapper
                        .lambda()
                        .eq(AppCode::getApplyClientId, codeCreateVO.getApplyClientId());
            }
            AppCode appCode = appCodeService.getOne(queryWrapper);
            // 获取当前时间 精确到秒
            Date currentDate = new Date();
            Long currentTime = currentDate.getTime();

            if (redisTime <= 0) {
                if (appCode != null) {
                    boolean saveFlag = false;
                    if (StringUtils.isNotBlank(codeCreateVO.getCodeColor())) {
                        if (codeCreateVO.getCodeColor().equals(appCode.getCodeColor())) {
                            appCode.setCodeColor(codeCreateVO.getCodeColor());
                            saveFlag = true;
                        }
                    }
                    if (StringUtils.isNotBlank(codeCreateVO.getBackColor())) {
                        if (codeCreateVO.getBackColor().equals(appCode.getBackColor())) {
                            appCode.setBackColor(codeCreateVO.getBackColor());
                            saveFlag = true;
                        }
                    }
                    if (StringUtils.isNotBlank(codeCreateVO.getImgPath())) {
                        if (codeCreateVO.getImgPath().equals(appCode.getImgPath())) {
                            appCode.setImgPath(codeCreateVO.getImgPath());
                            saveFlag = true;
                        }
                    }
                    code = appCode.getCode();
                    appCodeVO = Convert.convert(AppCodeVO.class, appCode);
                    if (saveFlag) {
                        appCodeService.saveOrUpdate(appCode);
                    }
                } else {
                    appCodeVO = Convert.convert(AppCodeVO.class, codeCreateVO);
                    appCodeVO.setApplyTime(currentTime);
                    appCodeVO.setExpireTime(redisTime);
                    code = CodeUtil.createCode(appCodeVO);
                    appCodeVO.setCode(code);
                    appCode = Convert.convert(AppCode.class, appCodeVO);

                    appCodeService.save(appCode);
                }
                redisHelper.set(getCodeKey, code);
                redisHelper.set(CacheConst.SYS_QRCODE_RS + code, appCodeVO);
            } else {
                boolean createFlag = false;
                if (appCode != null) {
                    appCodeService.removeById(appCode.getCodeId());
                    AppCodeHistory appCodeHistory = Convert.convert(AppCodeHistory.class, appCode);
                    appCodeHistory.setBegTime(new Date(appCode.getApplyTime()));
                    appCodeHistory.setEndTime(new Date());
                    appCodeHistory.setExpireDateTime(appCode.getExpireDateTime());
                    appCodeHistoryService.save(appCodeHistory);
                }
                Date expireDate =
                        DateUtils.addSeconds(currentDate, Integer.parseInt(redisTime.toString()));
                appCodeVO = Convert.convert(AppCodeVO.class, codeCreateVO);
                appCodeVO.setApplyTime(currentTime);
                appCodeVO.setExpireTime(redisTime);
                code = CodeUtil.createCode(appCodeVO);
                appCodeVO.setCode(code);
                appCode = Convert.convert(AppCode.class, appCodeVO);
                appCode.setExpireDateTime(expireDate.getTime());
                appCodeService.save(appCode);
                redisHelper.set(getCodeKey, code, redisTime);
                redisHelper.set(CacheConst.SYS_QRCODE_RS + code, appCodeVO, redisTime);
            }
        }
        return appCodeVO;
    }

    @Override
    @OpApi(
            funcCode = "APPCODE0002",
            title = "获取二维码基础信息",
            funcType = FuncTypeEnum.query,
            savelog = false)
    public AppCodeVO getCodeInfo(CodeDetailVO codeDetailVO) {
        return getCodeInfoNOCheck(codeDetailVO);
    }

    @Override
    @OpApi(
            funcCode = "APPCODE0003",
            title = "不校验获取二维码基础信息",
            funcType = FuncTypeEnum.query,
            publicFlag = BoolEnum.TRUE,
            savelog = false)
    public AppCodeVO getCodeInfoNOCheck(CodeDetailVO codeDetailVO) {
        return getCodeInfo(codeDetailVO.getCode());
    }

    @Override
    public AppCodeVO getCodeInfo(String code) {
        if (StringUtils.isBlank(code)) {
            return null;
        }
        if (!CodeUtil.checkShortLink(code)) {
            throw new AppException("校验未通过");
        }
        String key = CacheConst.SYS_QRCODE_RS + code;
        if (redisHelper.hasKey(key)) {
            AppCodeVO appCodeVO = (AppCodeVO) redisHelper.get(key);
            if (appCodeVO.getExpireTime() != null && appCodeVO.getExpireTime() != 0) {
                Long currentTime = System.currentTimeMillis();
                if (appCodeVO.getExpireTime() > 0) {
                    if ((currentTime - appCodeVO.getApplyTime()) / 1000
                            >= appCodeVO.getExpireTime()) {
                        redisHelper.del(key);
                        throw new AppException(ErrorEnum.SYSTEM_ERROR_CODE_invalid);
                    }
                }
                if (BoolEnum.TRUE.getType().equals(appCodeVO.getOnceFlag())) {
                    redisHelper.del(key);
                }
            }
            return appCodeVO;
        } else {
            throw new AppException(ErrorEnum.SYSTEM_ERROR_CODE_invalid);
        }
    }

    @Override
    @Async("oplogTaskExecutor")
    @Transactional
    public void removeCode(String code) {
        QueryWrapper<AppCode> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(AppCode::getCode, code);
        AppCode appCode = appCodeService.getOne(queryWrapper);
        if (appCode == null) {
            return;
        }
        appCodeService.removeById(appCode.getCodeId());
        AppCodeHistory appCodeHistory = Convert.convert(AppCodeHistory.class, appCode);
        appCodeHistory.setBegTime(new Date(appCode.getApplyTime()));
        appCodeHistory.setEndTime(new Date());
        appCodeHistoryService.save(appCodeHistory);
    }
}
